// JavaScript从编译到执行的过程，大致可分为四步：
// 1. 词法分析
// 2. 语法分析：检查代码是否存在错误，若存在错误，抛出；反之构建一颗抽象语法树（AST）
// 3. 预编译
// 4. 解释执行

// --预编译---
// JavaScript是解释性语言，也就是说，编译一行，执行一行，但js并非上来就进入编译环节，它在编译之前存在预编译过程。
// js中预编译一般有两种：全局的预编译和函数的预编译，分别发生在script内代码执行前和函数的执行前。
function test(a) {
    console.log(a);       
    var a = 123;
    console.log(a);       
    function a() {}
    console.log(a);       
    var b = function() {}
    console.log(b);       
    function d() {}
}
test(1)

// 是不是很诡异？ 若是，请继续，若不是，可离开
// 这往下就要好好讲讲预编译了
// 在预编译学习中，经常听到一句话：函数声明整体提升，变量声明提升。
// 这句话是预编译的纲要，要详细搞明白，我们详细唠唠
// ---函数预编译四部曲--
// 1. 预编译前，创建AO（Activation Obeject）对象
// 2. 查找所有形参和变量声明，作为AO对象的属性名，值为undefined
// 3. 将实参值赋值给形参
// 4. 查找函数声明，将函数名作为AO对象的属性名，函数体为值

// 具体分析
// * 创建AO,查找所有形参和变量,赋值undefined
const AO = {
    a: undefined, 
    b: undefined
}
// * 将实参值赋值给形参
AO.a = 1

// * 查找函数声明，函数名和函数值分别作为键和值
AO.a = function a() {}
AO.d = function d() {}

// 这时预编译完成，我们打印一下 AO
console.log(AO)
// { a: [Function: a], b: undefined, d: [Function: d] }

// 那么我们分析打印结果，是不是一目了然，大家在此有没有意识到你以为操作的是每个变量，其实是操作的是这些变量组成的AO对象

// -----------全局预编译--------------
// 全局不存在形参和实参，那么只需要处理变量和函数
// 全局预编译三部曲
// 1. 生成GO，(Global Object)
// 2. 查找变量声明，由于全局变量默认挂载window,若window当前已存在当前属性，忽略当前操作；若无，将变量名作为属性名，赋值undefined
// 3. 找函数声明，和变量类型，也是先查找函数是否有当前属性，若无，将函数作为属性名，函数体作为值

// 案例分析
console.log(a);
var a = 1;
console.log(a);
function test(a) {
    console.log(a)
}
test(2);

// 详细分析
// * 生成GO，查找变量声明函数声明后如下
const GO = {
    a: undefined,
    test: function(a){}
}

// 因此第一个a的值为undefined，随后a赋值为1，所以第二个a的值为1
// test中定义了变量a，因此打印的a为自身AO中的值。如果test中没有定义a，就会沿着作用域链，当GO中查找a。

